package model

import (
	"fmt"
	"smart-sensor-cloud/pkg/fsm"
	"time"
)

const (
	DeviceStatusNormal   = 0
	DeviceStatusWarning  = 1
	DeviceStatusAbnormal = 2
)

type (
	DeviceStatus int
	Device       struct {
		Model
		ProjectID           int          `json:"project_id"`
		Name                string       `json:"name"`
		DeviceNo            string       `json:"device_no"`
		Address             string       `json:"address"`
		Description         string       `json:"description"`
		TypeID              int          `json:"type_id"`
		TypeName            string       `json:"type_name"`
		SectionID           string       `json:"section_id"`
		SectionAddress      string       `json:"section_address"`
		UnitID              string       `json:"unit_id"`
		UnitAddress         string       `json:"unit_address"`
		HouseID             string       `json:"house_id"`
		HouseAddress        string       `json:"house_address"`
		HoID                string       `json:"ho_id"`
		WuyeUnitID          string       `json:"wuye_unit_id"`
		WuyeHouseID         string       `json:"wuye_house_id"`
		WuyeSectionID       string       `json:"wuye_section_id"`
		WuyeHoID            string       `json:"wuye_ho_id"`
		CoordinateX         float64      `json:"coordinate_x"`
		CoordinateY         float64      `json:"coordinate_y"`
		CoordinateZ         float64      `json:"coordinate_z"`
		CoordinateUpdatedAt time.Time    `json:"coordinate_updated_at"`
		Remark              string       `json:"remark"`
		Status              DeviceStatus `json:"status"`
		PlatformStatus      string       `json:"platform_status"`
		StatusCN            string       `json:"status_cn" gorm:"-"`
		BindUnit            bool         `json:bindunit`
		AssignTo            string       `json:assgin_to`
	}
)

func (status DeviceStatus) String() string {
	switch status {
	case DeviceStatusNormal:
		return "正常"
	case DeviceStatusWarning:
		return "告警"
	case DeviceStatusAbnormal:
		return "异常"
	}
	return ""
}

func (d *Device) AfterSave() {
	d.StatusCN = d.Status.String()
}

func (d *Device) AfterCreate() {
	d.StatusCN = d.Status.String()
}

func (d *Device) AfterUpdate() {
	d.StatusCN = d.Status.String()
}

func (d Device) TableName() string {
	return "devices"
}

func GetDevice(id int) (*Device, error) {
	device := new(Device)
	device.ID = uint(id)
	if err := db.First(device).Error; err != nil {
		return nil, fmt.Errorf("无法找到ID为%d的设备", id)
	}
	return device, nil
}

func GetDevices(index, size int) (*[]Device, int, error) {
	devices := make([]Device, 0)
	total := 0
	if err := db.Model(&Device{}).Count(&total).Error; err != nil {
		return nil, -1, err
	}

	if err := db.Model(&Device{}).Offset((index - 1) * size).Limit(size).Find(&devices).Error; err != nil {
		return nil, -1, err
	}
	return &devices, total, nil
}

func AddDevice(device *Device) error {
	if err := db.Create(device).Error; err != nil {
		return fmt.Errorf("添加设备失败")
	}
	return nil
}

func UpdateDevice(id int, change *Device) error {
	device, err := GetDevice(id)
	if err != nil {
		return err
	}

	if err := db.Model(device).Updates(change).Error; err != nil {
		return err
	}
	return nil
}

func DeviceConfirm(stateMachine *fsm.StateMachine, id int, typ string) error {
	device, err := GetDevice(id)
	if err != nil {
		return err
	}

	event := ""
	switch typ {
	case "app":
		event = DeviceEventAppConfirmed
	case "web":
		event = DeviceEventWebConfirmed
	}

	tx := db.Begin()

	fsmCtx := fsm.NewContext()
	fsmCtx.Set(DeviceContextKey, device)
	fsmCtx.Set(DBHandlerContextKey, tx)

	if err := stateMachine.Fire(device.PlatformStatus, event, false, fsmCtx); err != nil {
		tx.Rollback()
		return err
	}

	tx.Commit()
	return nil
}

func DeviceInitDelete(stateMachine *fsm.StateMachine, id int) error {
	device, err := GetDevice(id)
	if err != nil {
		return err
	}

	event := DeviceEventInitDeleted

	tx := db.Begin()

	fsmCtx := fsm.NewContext()
	fsmCtx.Set(DeviceContextKey, device)
	fsmCtx.Set(DBHandlerContextKey, tx)

	if err := stateMachine.Fire(device.PlatformStatus, event, false, fsmCtx); err != nil {
		tx.Rollback()
		return err
	}

	tx.Commit()
	return nil
}

func (d *Device) Delete() error {
	if err := db.Delete(d).Error; err != nil {
		return err
	}
	return nil
}
